package references

import (
    "jvmgo/ch07/instructions/base"
    "jvmgo/ch07/rtda"
    "jvmgo/ch07/rtda/heap"
)

// 调用实例方法
// 专门调用 超类、私有、实例初始化方法
type INVOKE_SPECIAL struct {
    base.Index16Instruction
}

// hack!
func (self *INVOKE_SPECIAL) Execute(frame *rtda.Frame) {
    currentClass := frame.Method().Class()
    cp := currentClass.ConstantPool()
    methodRef := cp.GetConstant(self.Index).(*heap.MethodRef)
    resolvedClass := methodRef.ResolvedClass()
    resolvedMethod := methodRef.ResolvedMethod()

    if resolvedMethod.Name() == "<init>"&&resolvedMethod.Class() != resolvedClass {
        panic("java.lang.NoSuchMethodError")
    }

    if resolvedMethod.IsStatic() {
        panic("java.lang.IncompatibleClassChangeError")
    }

    ref := frame.OperandStack().GetRefFromTop(resolvedMethod.ArgSlotCount() - 1)
    if ref == nil {
        panic("java.lang.NullPointerException")
    }

    if resolvedMethod.IsProtected() &&
        resolvedMethod.Class().IsSuperClassOf(currentClass) &&
        resolvedMethod.Class().GetPackageName() != currentClass.GetPackageName() &&
        ref.Class() != currentClass &&
        !ref.Class().IsSubClassOf(currentClass) {
        panic("java.lang.IllegalAccessError")
    }

    methodToBeInvoked := resolvedMethod
    if currentClass.IsSuper() &&
        resolvedClass.IsSuperClassOf(currentClass) &&
        resolvedMethod.Name() != "<init>" {

        methodToBeInvoked = heap.LookupMethodInClass(currentClass.SuperClass(),
            methodRef.Name(), methodRef.Descriptor())
    }

    if methodToBeInvoked == nil || methodToBeInvoked.IsAbstract() {
        panic("java.lang.AbstractMethodError")
    }

    base.InvokeMethod(frame, methodToBeInvoked)
}
